/*
 * Copyright (c) 2011, Shane Conder and Lauren Darcey
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are 
 * permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this list of 
 *   conditions and the following disclaimer.
 *   
 * * Redistributions in binary form must reproduce the above copyright notice, this list 
 *   of conditions and the following disclaimer in the documentation and/or other 
 *   materials provided with the distribution.
 *   
 * * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used
 *   to endorse or promote products derived from this software without specific prior 
 *   written permission.
 *   
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * <ORGANIZATION> = Mamlambo
 */
package com.mamlambo.easy3d.helpers;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;

public class EasyCube {
    private float mSize;

    private FloatBuffer mVertexBuffer;
    private FloatBuffer mNormalBuffer;
    private ByteBuffer mIndexBuffer;

    EasyCube(float size) throws IllegalArgumentException {
        if (size < 0.0f) {
            throw new IllegalArgumentException(
                    "Only use positive size values to CubeSmallGLUT");
        }

        // scale unit cube to this size
        mSize = size;

        if (mSize != 1.0f) {
            for (int vertex = 0; vertex < vertices.length; vertex++) {
                vertices[vertex] *= mSize;
            }
        }

        mVertexBuffer = OpenGLUtilities.getFloatBufferFromFloatArray(vertices);
        mNormalBuffer = OpenGLUtilities.getFloatBufferFromFloatArray(normals);
        mIndexBuffer = OpenGLUtilities.getByteBufferFromByteArray(indices);
    }

    // center our unit cube around the origin so translations make sense
    float vertices[] = {
            // front
            -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
            -0.5f,
            0.5f,
            -0.5f,
            -0.5f,
            0.5f,

            // back
            0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f,
            -0.5f,
            0.5f,
            -0.5f,
            -0.5f,

            // top
            -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f,
            -0.5f,
            0.5f,
            0.5f,

            // bottom
            -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f,
            -0.5f,
            -0.5f,

            // right
            0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f,
            -0.5f, 0.5f,

            // left
            -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f,
            -0.5f, -0.5f, };

    byte indices[] = {
            // front
            0, 1, 2, 2, 3, 0,
            // back
            4, 5, 6, 6, 7, 4,
            // top
            8, 9, 10, 10, 11, 8,
            // bottom
            12, 13, 14, 14, 15, 12,
            // right
            16, 17, 18, 18, 19, 16,
            // left
            20, 21, 22, 22, 23, 20, };

    float normals[] = {
            // front
            0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
            // back
            0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
            // top
            0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
            // bottom
            0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
            // right
            1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
            // left
            -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, };

    void draw(GL10 gl) {
        // needed for correct culling, our vertices are clockwise
        gl.glFrontFace(GL10.GL_CW);

        // set our vertexes
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);

        // set our normals for correct lighting
        gl.glNormalPointer(GL10.GL_FLOAT, 0, mNormalBuffer);

        // draw the elements
        gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,
                GL10.GL_UNSIGNED_BYTE, mIndexBuffer);

    }

    void drawAgain(GL10 gl) {
        // used to draw again when the vertex and normal pointers haven't
        // changed -- won't work otherwise

        // draw the elements -- assuming all of the above prep has taken place
        gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,
                GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
    }

    // we do it this way to demonstrate separately the work that is needed for
    // adding textures
    public static class Textured extends EasyCube {

        public Textured(float size) throws IllegalArgumentException {
            super(size);
        }

        FloatBuffer mCoordinateBuffer;
        float textureCoords[] = { 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0,
                0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1,
                0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, };

        int mTextureID;
        boolean textureEnabled = false;

        public void setTexture(GL10 gl, Context c, int textureID, int drawableID) {
            mCoordinateBuffer = OpenGLUtilities
                    .getFloatBufferFromFloatArray(textureCoords);

            mTextureID = textureID;

            gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);

            Bitmap bitmap = BitmapFactory.decodeResource(c.getResources(),
                    drawableID);
            Bitmap bitmapSq = Bitmap
                    .createScaledBitmap(bitmap, 512, 512, false);
            GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmapSq, 0);
            bitmap.recycle();
            bitmapSq.recycle();

            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
                    GL10.GL_NEAREST);
            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
                    GL10.GL_LINEAR);
            textureEnabled = true;
        }

        public void draw(GL10 gl) {
            if (textureEnabled) {
                gl.glEnable(GL10.GL_TEXTURE_2D);
                gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
                gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
                gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mCoordinateBuffer);
            }

            super.draw(gl);

            if (textureEnabled) {
                gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
                gl.glDisable(GL10.GL_TEXTURE_2D);
            }
        }

        // have to do a full draw due to texture state changes
        public void drawAgain(GL10 gl) {
            draw(gl);
        }

    }
}
